namespace Crux.Core.Data;
/// <summary>
/// StringIdentifier
/// </summary>
public class StringIdentifier : IComparable<StringIdentifier>, IEquatable<StringIdentifier>
{
    private static readonly long __random = CalculateRandomValue();
    private static Random __internalRandom = new Random();
    private static int __staticIncrement = __internalRandom.Next();
    private readonly int _a;
    private readonly int _b;
    private readonly int _c;
    private StringIdentifier(int a, int b, int c)
    {
        _a = a;
        _b = b;
        _c = c;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public static StringIdentifier GenerateNewId() => GenerateNewId(GetTimestampFromDateTime(DateTime.UtcNow));
    /// <summary>
    /// 
    /// </summary>
    /// <param name="timestamp"></param>
    /// <returns></returns>
    public static StringIdentifier GenerateNewId(int timestamp)
    {
        int increment = Interlocked.Increment(ref __staticIncrement) & 0x00ffffff; // only use low order 3 bytes
        return Create(timestamp, __random, increment);
    }
    private static long CalculateRandomValue()
    {
        var seed = (int)DateTime.UtcNow.Ticks ^ GetMachineHash() ^ GetPid();
        var random = new Random(seed);
        var high = random.Next();
        var low = random.Next();
        var combined = (long)((ulong)(uint)high << 32 | (ulong)(uint)low);
        return combined & 0xffffffffff;
    }
    private static StringIdentifier Create(int timestamp, long random, int increment)
    {
        if (random < 0 || random > 0xffffffffff)
        {
            throw new ArgumentOutOfRangeException(nameof(random), "The random value must be between 0 and 1099511627775 (it must fit in 5 bytes).");
        }
        if (increment < 0 || increment > 0xffffff)
        {
            throw new ArgumentOutOfRangeException(nameof(increment), "The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
        }
        var a = timestamp;
        var b = (int)(random >> 8);
        var c = (int)(random << 24) | increment;
        return new StringIdentifier(a, b, c);
    }
    [MethodImpl(MethodImplOptions.NoInlining)]
    private static int GetCurrentProcessId() => (int)Environment.WorkingSet;
    private static int GetMachineHash()
    {
        var machineName = GetMachineName();
        return 0x00ffffff & machineName.GetHashCode();
    }
    private static string GetMachineName() => Environment.MachineName;
    private static short GetPid()
    {
        try
        {
            return (short)GetCurrentProcessId();
        }
        catch (SecurityException)
        {
            return 0;
        }
    }
    private static int GetTimestampFromDateTime(DateTime timestamp)
    {
        var secondsSinceEpoch = (long)Math.Floor((InternalUtils.ToUniversalTime(timestamp) - Constants.UnixEpoch).TotalSeconds);
        if (secondsSinceEpoch < uint.MinValue || secondsSinceEpoch > uint.MaxValue)
        {
            throw new ArgumentOutOfRangeException(nameof(timestamp));
        }
        return (int)(uint)secondsSinceEpoch;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="other"></param>
    /// <returns></returns> 
    public int CompareTo(StringIdentifier? other)
    {
        if (other is null) return -1;

        int result = ((uint)_a).CompareTo((uint)other._a);
        if (result != 0) { return result; }
        result = ((uint)_b).CompareTo((uint)other._b);
        if (result != 0) { return result; }
        return ((uint)_c).CompareTo((uint)other._c);
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="rhs"></param>
    /// <returns></returns>
    public bool Equals(StringIdentifier? rhs) => _a == rhs?._a &&
            _b == rhs._b &&
            _c == rhs._c;
    /// <summary>
    /// 
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public override bool Equals(object? obj)
    {
        if (null == obj) return false;
        if (obj is StringIdentifier identifier)
        {
            return Equals(identifier);
        }
        else
        {
            return false;
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public override int GetHashCode()
    {
        int hash = 17;
        hash = 37 * hash + _a.GetHashCode();
        hash = 37 * hash + _b.GetHashCode();
        hash = 37 * hash + _c.GetHashCode();
        return hash;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        var c = new char[24];
        c[0] = InternalUtils.ToHexChar((_a >> 28) & 0x0f);
        c[1] = InternalUtils.ToHexChar((_a >> 24) & 0x0f);
        c[2] = InternalUtils.ToHexChar((_a >> 20) & 0x0f);
        c[3] = InternalUtils.ToHexChar((_a >> 16) & 0x0f);
        c[4] = InternalUtils.ToHexChar((_a >> 12) & 0x0f);
        c[5] = InternalUtils.ToHexChar((_a >> 8) & 0x0f);
        c[6] = InternalUtils.ToHexChar((_a >> 4) & 0x0f);
        c[7] = InternalUtils.ToHexChar(_a & 0x0f);
        c[8] = InternalUtils.ToHexChar((_b >> 28) & 0x0f);
        c[9] = InternalUtils.ToHexChar((_b >> 24) & 0x0f);
        c[10] = InternalUtils.ToHexChar((_b >> 20) & 0x0f);
        c[11] = InternalUtils.ToHexChar((_b >> 16) & 0x0f);
        c[12] = InternalUtils.ToHexChar((_b >> 12) & 0x0f);
        c[13] = InternalUtils.ToHexChar((_b >> 8) & 0x0f);
        c[14] = InternalUtils.ToHexChar((_b >> 4) & 0x0f);
        c[15] = InternalUtils.ToHexChar(_b & 0x0f);
        c[16] = InternalUtils.ToHexChar((_c >> 28) & 0x0f);
        c[17] = InternalUtils.ToHexChar((_c >> 24) & 0x0f);
        c[18] = InternalUtils.ToHexChar((_c >> 20) & 0x0f);
        c[19] = InternalUtils.ToHexChar((_c >> 16) & 0x0f);
        c[20] = InternalUtils.ToHexChar((_c >> 12) & 0x0f);
        c[21] = InternalUtils.ToHexChar((_c >> 8) & 0x0f);
        c[22] = InternalUtils.ToHexChar((_c >> 4) & 0x0f);
        c[23] = InternalUtils.ToHexChar(_c & 0x0f);
        return new string(c);
    }
}